package es.deusto.ingenieria.is.search.tsp.formulation;


import es.deusto.ingenieria.is.search.formulation.Operator;
import es.deusto.ingenieria.is.search.formulation.State;

/**
 * @author Kevin e Inigo
 * Clase que va ha representar nuestra unica accion, la cual consiste en anadir una ciudad 
 * que hemos visitado en la lista de ciudades visitadas.
 * Hereda de Opertor, la cual se encuentra en la libreria suministrada.
 */
public class AnadirCiudad extends Operator{

	/**
	 * Proxima ciudad que vamos a visitar
	 */
	private Ciudad ciudadAnadir;
	
	/** Constructor pasandole un objeto ciudad, la cual seria la ciudad a anadir
	 * @param Ciudad anadir
	 */
	public AnadirCiudad(Ciudad anadir) {

		this.ciudadAnadir=anadir;
		this.setName("Avanzar a ciudad "+anadir.getNombre());
	}

	/* (non-Javadoc)
	 * @see es.deusto.ingenieria.is.search.formulation.Operator#effect(es.deusto.ingenieria.is.search.formulation.State)
	 * EN este metodo implementamos la funcionalidad de la accion. En primer lugar sacamos dicha ciudad del array de NoVisitadas del enviroment y
	 * la llevamos al array de Visitadas. A continuacion calculamos el costo de ejecutar dicha accion.
	 */
	@Override
	protected State effect(State state) {

		EnvironmentMap enviroment = (EnvironmentMap)((EnvironmentMap) state).clone();
		System.out.println(enviroment);
		enviroment.borrarCiudad(ciudadAnadir);
		enviroment.anadirCiudad(ciudadAnadir);

		double xtemp = 0.0;
		double ytemp = 0.0;
		/*Para calcular el coste, primero comprobamos que ya haya alguna ciudad visitada, es decir que no estemos en la ciudad inicial. Ya que en dicho caso
		 * debemos tomar como ciudad de origen la ciudad inical.
		 * 
		 */
		if(enviroment.ciudadesVisitadas.size() > 1){
			xtemp = Math.pow(ciudadAnadir.getX()-enviroment.ciudadesVisitadas.get(enviroment.ciudadesVisitadas.size() - 2).x,2);
			ytemp = Math.pow(ciudadAnadir.getY()-enviroment.ciudadesVisitadas.get(enviroment.ciudadesVisitadas.size() - 2).y,2);
		}else{
			xtemp = Math.pow(ciudadAnadir.getX()-enviroment.cInicial.x,2);
			ytemp = Math.pow(ciudadAnadir.getY()-enviroment.cInicial.y,2);
		}

		double coste = Math.sqrt(xtemp + ytemp);
		System.out.println(this.toString());
		
		//En caso de que sea la última ciudad a añadir, adicionalmente añado el coste desde esa al final
		// Ya que la última añade la distancia desde la anterior a ella
		if(enviroment.ciudadesNoVisitadas.size()==0)
		{
			xtemp = Math.pow(enviroment.cFinal.x-enviroment.ciudadesVisitadas.get(enviroment.ciudadesVisitadas.size() - 1).x,2);
			ytemp = Math.pow(enviroment.cFinal.y-enviroment.ciudadesVisitadas.get(enviroment.ciudadesVisitadas.size() - 1).y,2);
			enviroment.costeTotal+=Math.sqrt(xtemp + ytemp);
		}
		enviroment.costeTotal+=coste;
		this.setCost(coste);

		return enviroment;
	}

	/* (non-Javadoc)
	 * @see es.deusto.ingenieria.is.search.formulation.Operator#isApplicable(es.deusto.ingenieria.is.search.formulation.State)
	 * Metodo en el que comprobamos si envirmoent ya dispones dicha ciudad como visitada o no.
	 * return boolean esVisitada
	 */
	@Override
	protected boolean isApplicable(State state) {
		EnvironmentMap environment = (EnvironmentMap) state;
		return !environment.comprobarCiudad(ciudadAnadir);
	}

	/**Metodo util para comprobar el funcionamiento correcto del metodo effect. 
	 * @param env
	 * @return EnvironmentMap
	 */
	public EnvironmentMap prueba(EnvironmentMap env){

		return (EnvironmentMap) this.effect(env);

	}

	/**Metodo util para comprobar el funcionamiento correcto del metodo isApplicable.
	 * @param env
	 */
	public void pruebaAplicable(EnvironmentMap env){
		System.out.println("*Es aplicable: " + isApplicable(env));
	}

}
